package ca.michaelmaguire.server.mobiledownload.server;

import com.google.appengine.api.datastore.Blob;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

import ca.michaelmaguire.server.mobiledownload.server.entity.AppContents;
import ca.michaelmaguire.server.mobiledownload.server.entity.AppEvent;
import ca.michaelmaguire.server.mobiledownload.server.entity.UserInfo;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class FileDownloadServlet extends BaseRecordingServlet {
	/**
	 * 
	 */
	private static final long serialVersionUID = -808894634175346118L;
	private static final Logger log = Logger
			.getLogger(FileDownloadServlet.class.getName());

	public void doGet(HttpServletRequest aHttpServletRequest,
			HttpServletResponse aHttpServletResponse) throws ServletException,
			IOException {
		try {
			PersistenceManager pm = PMF.get().getPersistenceManager();

			try {

				/*
				 * String responseString = ""; responseString += "Parameters: ";
				 * Enumeration parameterNames = aHttpServletRequest
				 * .getParameterNames(); while
				 * (parameterNames.hasMoreElements()) { String parameter =
				 * (String) parameterNames.nextElement(); responseString += " "
				 * + parameter; String[] value = aHttpServletRequest
				 * .getParameterValues(parameter); responseString += " " +
				 * value[0];
				 * 
				 * }
				 */

				String appKeyString = HeaderUtilities
						.getAndSaveParameterOrHeader(aHttpServletRequest,
								HeaderConstants.NAME_APP);

				Key appKey = KeyFactory.stringToKey(appKeyString);

				AppContents appContents = pm.getObjectById(AppContents.class,
						appKey);

				String appFileName = appContents.getFileName();

				String mimeType = HeaderUtilities
						.produceMimeTypeForFileName(appFileName);
				if (null == mimeType) {
					mimeType = "text/html";
				}
				aHttpServletResponse.setContentType(mimeType);

				// Ensure you set the Content-Disposition with an attachment
				// filename otherwise many mobile
				// browsers (e.g. Opera on Nokia N86) won't understand you are
				// downloading a file that
				// can be installed and you'll just get a weird 'Could not
				// install
				// download_1' error.
				aHttpServletResponse.addHeader("Content-Disposition",
						"attachment; filename=" + appFileName);

				// Ensure that we mark our response so that browsers and mobile
				// transcoders don't attempt to cache or transform the returned
				// files.
				aHttpServletResponse.addHeader("Pragma", "no-cache");
				aHttpServletResponse
						.addHeader("Cache-Control",
								"private, no-cache, no-store, proxy-revalidate, no-transform");

				UserInfo userInfo = recordEvent(aHttpServletRequest,
						appContents.getKey(), AppEvent.DOWNLOAD);

				if (mimeType.equals(HeaderConstants.JAD_MIME_TYPE)) {
					doJadProcessing(appContents, userInfo, aHttpServletResponse);
				} else {
					// Default is just to write the contents out directly.
					doDefaultOutput(appContents, userInfo, aHttpServletResponse);
				}

			} finally {
				pm.close();
			}
		} catch (Exception aException) {
			throw new ServletException(aException);
		}
	}

	private void doDefaultOutput(AppContents aAppContents, UserInfo aUserInfo,
			HttpServletResponse aHttpServletResponse) throws IOException {
		Blob blob = aAppContents.getContent();

		ServletOutputStream sos = aHttpServletResponse.getOutputStream();
		sos.write(blob.getBytes());
		sos.flush();
	}

	private void doJadProcessing(AppContents aAppContents, UserInfo aUserInfo,
			HttpServletResponse aHttpServletResponse) throws IOException {
		Blob blob = aAppContents.getContent();

		aHttpServletResponse
				.setCharacterEncoding(HeaderConstants.JAD_CHARACTER_ENCODING_ISO_8859_1);

		ByteArrayInputStream bais = new ByteArrayInputStream(blob.getBytes());
		InputStreamReader isr = new InputStreamReader(bais);
		BufferedReader bufferedReader = new BufferedReader(isr);

		Hashtable<String, String> lines = new Hashtable<String, String>();

		try {
			String rawLine = bufferedReader.readLine();
			while (null != rawLine) {
				// Process the line by doing our own 'standard' substitutions.
				String processedLine = doJadLineSubstitutions(aAppContents,
						aUserInfo, rawLine);

				int indexOfColon = processedLine.indexOf(':');
				String before = processedLine.substring(0, indexOfColon).trim();
				String after = processedLine.substring(indexOfColon + 1,
						processedLine.length()).trim();

				lines.put(before, after);

				rawLine = bufferedReader.readLine();
			}
		} catch (IOException ioe) {
			log.warning("IOError reading jad file: " + ioe.getMessage());
		}

		// Now do some special case additions and replacement to specified keys.

		doManualValueReplacementsAndAdditions(aAppContents, aUserInfo, lines);

		// Write the lines out.

		ServletOutputStream sos = aHttpServletResponse.getOutputStream();
		try {

			OutputStreamWriter osw = new OutputStreamWriter(sos,
					HeaderConstants.JAD_CHARACTER_ENCODING_ISO_8859_1);
			try {
				Enumeration<String> keys = lines.keys();
				while (keys.hasMoreElements()) {
					String key = keys.nextElement();
					String value = lines.get(key);
					osw.write(key);
					osw.write(": ");
					osw.write(value);
					osw.write("\r\n");
				}
			} finally {
				osw.close();
			}
		} finally {
			sos.close();
		}
	}

	private String doJadLineSubstitutions(AppContents aAppContents,
			UserInfo aUserInfo, String aRawLine) {
		String returnValue = aRawLine;
		returnValue = returnValue.replaceAll("<"
				+ HeaderConstants.NAME_SITE_BASE_URL + ">",
				"http://mobiledownload.appspot.com");
		returnValue = returnValue.replaceAll("<" + HeaderConstants.NAME_USER
				+ ">", aUserInfo.toString());
		returnValue = returnValue.replaceAll("<" + HeaderConstants.NAME_APP
				+ ">", KeyFactory.keyToString(aAppContents.getKey()));

		Key secondaryFileKey = aAppContents.getSecondaryAppContents();
		if (null != secondaryFileKey) {
			String secondaryFileKeyString = KeyFactory
					.keyToString(secondaryFileKey);
			returnValue = returnValue.replaceAll("<"
					+ HeaderConstants.NAME_SECONDARY_APP_CONTENTS + ">",
					secondaryFileKeyString);
		}
		return returnValue;
	}

	private void doManualValueReplacementsAndAdditions(
			AppContents aAppContents, UserInfo aUserInfo,
			Hashtable<String, String> aLines) {
		// Because (e.g. with EclipseME) it can mess up the build/test cycle to
		// use anything other than
		// MIDlet-Jar-Url: SampleMIDlet.jar, we manually replace the jar URL
		// here as needed.

		Key secondaryFileKey = aAppContents.getSecondaryAppContents();
		if (null != secondaryFileKey) {
			String secondaryFileKeyString = KeyFactory
					.keyToString(secondaryFileKey);

			String userInfoKeyString = KeyFactory.keyToString(aUserInfo
					.getKey());

			aLines.put(HeaderConstants.NAME_MIDLET_JAR_URL,
					"http://mobiledownload.appspot.com/download?"
							+ HeaderConstants.NAME_APP + "="
							+ secondaryFileKeyString + "&"
							+ HeaderConstants.NAME_USER + "="
							+ userInfoKeyString);
		}
	}
}